<?php

namespace Aitels\Design;

use Illuminate\Console\Command;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * 数据库设计工具
 * Class Designer
 * @package Aitels\Design
 */
class Designer
{
    protected $provider;
    protected $processor;
    protected $manager;
    protected $console;


    public function __construct(DefinitionProvider $provider, DefinitionProcessor $processor, TableManager $tableManager, Command $command)
    {

        $this->provider = $provider;
        $this->processor = $processor;
        $this->manager = $tableManager;
        $this->console = $command;
    }


    public function design()
    {
        $definitions = $this->getDefinitions();


        foreach ($definitions as $definition) {

            //数据库表名(英文蛇形名称)
            $table = $definition['tableName'];

            //判断与本地缓存的数据表定义记录是否一致，不一致则重新生成
            if ($this->manager->hasChange($table, $definition)) {
                //不支持备份数据量太大的表(5000条记录),开发阶段通常也不会有这么多数据
                //询问用户是否继续操作(只保留5000条记录)
                //确实要design大表的话建议建一个空数据库来design，然后再通过第三方结构同步工具同步至开发数据库
                if ($this->isBigTable($table)) {
                    if ($this->userChooseToSkip($table)) {
                        continue;
                    }
                }
                //首先备份一下旧表定义、数据
                $this->manager->backup($table);
                //接着我们把旧表删了
                $this->manager->drop($table);
                //创建新表
                $this->manager->create($table, $definition);

                try {
                    //创建后回填旧表的数据
                    $this->manager->refill($table, $definition);
                    $this->manager->storeDesignHistory($table, $definition);
                    echo "design $table success ! \n";

                } catch (QueryException $e) {

                    echo "design $table failed ! \n";
                    //很不幸，插入失败，多数情况下是修改字段类型不兼容导致的。
                    //由于之前已经备份，我们选择还原旧表
                    $this->manager->recover($table);
                    $message = mb_substr($e->getMessage(), 0, 100);
                    echo("[错误]:$message, 出现这种情况的原因可能是修改字段的类型不一致。");

                } catch (FileNotFoundException $e) {

                }
            } else {
                echo "$table unchanged , skip .\n";
            }
        }
    }

    public function userChooseToSkip($table)
    {
        do {
            $confirm = $this->console->anticipate("Can not backup table because $table has more than 5000 records .Do you want to continue this operation(only 5000 records will be saved) ? (yes/no)", ['yes', 'no']);
        } while (!($confirm == 'yes' || $confirm == 'no'));
        return $confirm === 'no';
    }

    public function isBigTable($table)
    {
        if (Schema::hasTable($table))
            return DB::select("select count(1) as total from $table;")[0]->total > 5000;
        return false;
    }


    public function getDefinitions()
    {
        $definitions = collect($this->provider->provide())
            ->map(function ($definition) {
                return $this->processor->process($definition);
            });

        file_put_contents(storage_path('design/cache.php'), '<?php' . PHP_EOL . 'return  ' . var_export($definitions->all(), true) . ';');

        return $definitions;
    }
}
